#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <set>

namespace zl
{
    namespace server
    {
        // 服务提供者管理
        class ProviderManager
        {
        public:
            using ptr = std::shared_ptr<ProviderManager>;

            // 提供者
            struct Provider
            {
                using ptr = std::shared_ptr<Provider>;
                std::mutex m_mutex;
                BaseConnection::ptr m_conn;
                Address m_host;
                std::vector<std::string> m_methods;

                // 提供主机信息和连接
                Provider(const BaseConnection::ptr &conn, const Address &host)
                    : m_conn(conn), m_host(host)
                {
                }

                // 添加可单个server支持的服务
                void appendMethod(const std::string &method)
                {
                    std::lock_guard<std::mutex> lock(m_mutex);
                    m_methods.emplace_back(method);
                }
            };

            // 当一个新的服务提供者进行服务注册时才调用
            void addProvider(const BaseConnection::ptr &c, const Address &host, const std::string &method)
            {
                Provider::ptr Newprovider;

                {
                    std::unique_lock<std::mutex> lock(m_mutex);
                    auto it = _conns.find(c);
                    if (it != _conns.end())
                    {
                        Newprovider = it->second;
                    }
                    else
                    {
                        Newprovider = std::make_shared<Provider>(c, host);
                        // 新增连接
                        _conns[c] = Newprovider;
                    }

                    // 服务的提供者增加一个，m_providers新增
                    // 如果method已经存在，则增加一个，否则创建一个
                    m_providers[method].insert(Newprovider);
                }
                // 向服务对象中新增一个所能提供的服务名称
                Newprovider->appendMethod(method);
            }

            // 当一个服务提供者断开连接的时候，获取他的信息--用于进行服务下线通知
            Provider::ptr getProvider(const BaseConnection::ptr &conn)
            {
                std::unique_lock<std::mutex> lock(m_mutex);
                auto it = _conns.find(conn);
                if (it != _conns.end())
                {
                    return it->second;
                }

                return Provider::ptr();
            }

            // 当一个服务提供者断开连接的时候，删除它的关联信息
            void removeProvider(const BaseConnection::ptr &conn)
            {
                std::unique_lock<std::mutex> lock(m_mutex);
                auto it = _conns.find(conn);
                if (it == _conns.end())
                {
                    // 当前断开连接的不是一个服务提供者
                    return;
                }

                // 提供者 要删除这个提供者所提供的所有服务
                for (auto &method : it->second->m_methods)
                {
                    m_providers[method].erase(it->second);
                }

                // 删除连接与服务提供者的关联关系
                _conns.erase(it);
            }

            // 该方法可提供的所有提供者的主机信息
            std::vector<Address> methodHosts(const std::string &method)
            {
                std::unique_lock<std::mutex> lock(m_mutex);
                auto it = m_providers.find(method);
                if (it == m_providers.end())
                {
                    return std::vector<Address>();
                }

                std::vector<Address> result;
                for (auto &provider : it->second)
                {
                    result.push_back(provider->m_host);
                }
                return result;
            }

        private:
            std::mutex m_mutex;
            // 服务 提供者
            std::unordered_map<std::string, std::set<Provider::ptr>> m_providers;
            // 连接 提供者
            std::unordered_map<BaseConnection::ptr, Provider::ptr> _conns;
        };

        // 服务发现者管理
        class DiscoverManager
        {
        public:
            using ptr = std::shared_ptr<DiscoverManager>;

            // 发现者
            struct Discoverer
            {
                using ptr = std::shared_ptr<Discoverer>;
                std::mutex m_mutex;
                BaseConnection::ptr m_conn;         // 发现者关联的客户端连接
                std::vector<std::string> m_methods; // 发现过的服务名称

                // 发现者只提供连接
                Discoverer(const BaseConnection::ptr &conn)
                    : m_conn(conn)
                {
                }

                // 添加可单个client发现的服务
                void appendMethod(const std::string &method)
                {
                    std::lock_guard<std::mutex> lock(m_mutex);
                    m_methods.emplace_back(method);
                }
            };

            // 当每次客户端进行服务发现的时候新增发现者，新增服务名称
            Discoverer::ptr addDiscovrer(const BaseConnection::ptr &conn, const std::string &method)
            {
                Discoverer::ptr discoverer;
                {
                    std::unique_lock<std::mutex> lock(m_mutex);

                    auto it = _conns.find(conn);
                    if (it != _conns.end())
                    {
                        discoverer = it->second;
                    }
                    else
                    {
                        discoverer = std::make_shared<Discoverer>(conn);
                        _conns.insert(std::make_pair(conn, discoverer));
                    }
                    m_discoverers[method].insert(discoverer);
                }
                discoverer->appendMethod(method);
                return discoverer;
            }

            // 当一个客户断开连接的时候，找到发现者信息，删除关联数据
            void removeDiscoverer(const BaseConnection::ptr &conn)
            {
                std::unique_lock<std::mutex> lock(m_mutex);
                auto it = _conns.find(conn);
                if (it == _conns.end())
                {
                    // 没有找到连接对应的发现者信息，代表客户端不是一个服务发现者
                    return;
                }

                for (auto &method : it->second->m_methods)
                {
                    m_discoverers[method].erase(it->second);
                }

                // 删除连接与服务提供者的关联关系
                _conns.erase(it);
            }

            // 当有一个新的服务提供者上线，则进行上线通知
            void onlieNotify(const std::string &method, const Address &addr)
            {
                return notify(method, addr, ServiceOptype::SERVICE_ONLINE);
            }
            // 当有一个服务提供者断开连接，则进行下线通知
            void offlieNotify(const std::string &method, const Address &addr)
            {
                return notify(method, addr, ServiceOptype::SERVICE_OFFLINE);
            }

        private:
            void notify(const std::string &method, const Address &host, ServiceOptype optype)
            {
                std::unique_lock<std::mutex> lock(m_mutex);
                auto it = m_discoverers.find(method);
                if (it == m_discoverers.end())
                {
                    // 这个服务没有发现者
                    return;
                }

                auto req_msg = zl::MessageFactory::create<ServiceRequest>();
                req_msg->setHost(host);
                req_msg->setId(UUID::uuid());
                req_msg->setMethod(method);
                req_msg->setOptype(optype);
                req_msg->setMType(MType::REQ_SERVICE);

                for (auto &discoverer:it->second)
                {
                    discoverer->m_conn->send(req_msg);
                }
            }

        private:
            std::mutex m_mutex;
            // 提供者提供的服务 发现者
            std::unordered_map<std::string, std::set<Discoverer::ptr>> m_discoverers;
            // 连接 发现者
            std::unordered_map<BaseConnection::ptr, Discoverer :: ptr> _conns;
        };

        // 服务注册中心管理
        class PDManager
        {
        public:
            using ptr = std::shared_ptr<PDManager>;

            PDManager() : m_provider_manager(std::make_shared<ProviderManager>())
            , m_discoverer_manager(std::make_shared<DiscoverManager>())
            {
            }

            void onServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                // 服务操作请求：服务注册/服务发现/
                ServiceOptype optype = msg->optype();
                // 服务注册
                //  1. 新增服务提供者；  2. 进行服务上线的通知
                if (optype == ServiceOptype::SERVICE_REGISTRY)
                {
                    ILOG("%s %d 注册服务 %s", msg->host().first.c_str(), msg->host().second, msg->method().c_str());
                    m_provider_manager->addProvider(conn, msg->host(), msg->method());
                    m_discoverer_manager->onlieNotify(msg->method(), msg->host());

                    return registryResponse(conn, msg);
                }
                else if (optype == ServiceOptype::SERVICE_DISCOVERY)
                {
                    // 服务发现
                    //  1. 新增服务发现者
                    ILOG("客户端要进行 %s 服务发现！", msg->method().c_str());
                    m_discoverer_manager->addDiscovrer(conn, msg->method());

                    return discoverResponse(conn, msg);
                }
                else
                {
                    ELOG("收到服务操作请求，但是操作类型错误！");
                    return errorResponse(conn, msg);
                }
            }

            void onConnShutdown(const BaseConnection::ptr &conn)
            {
                auto provider = m_provider_manager->getProvider(conn);
                if (provider.get() != nullptr)
                {
                    ILOG("%s:%d 服务下线", provider->m_host.first.c_str(), provider->m_host.second);
                    for (auto &method : provider->m_methods)
                    {
                        m_discoverer_manager->offlieNotify(method, provider->m_host);
                    }
                    m_provider_manager->removeProvider(conn);
                }
                m_provider_manager->removeProvider(conn);
            }

        private:
            void errorResponse(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                auto rsp = MessageFactory::create<ServiceResponse>();
                rsp->setId(msg->rid());
                rsp->setMType(MType::RSP_SERVICE);
                rsp->setRCode(RCode::RCODE_INVALID_OPTYPE);
                rsp->setOptype(ServiceOptype::SERVICE_UNKNOW);

                conn->send(rsp);
            }

            void registryResponse(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                auto rsp = MessageFactory::create<ServiceResponse>();
                rsp->setId(msg->rid());
                rsp->setMType(MType::RSP_SERVICE);
                rsp->setRCode(RCode::RECODE_OK);
                rsp->setOptype(ServiceOptype::SERVICE_REGISTRY);

                conn->send(rsp);
            }

            void discoverResponse(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                auto rsp = MessageFactory::create<ServiceResponse>();
                rsp->setId(msg->rid());
                rsp->setMType(MType::RSP_SERVICE);
                rsp->setOptype(ServiceOptype::SERVICE_DISCOVERY);

                std::vector<Address> addrs = m_provider_manager->methodHosts(msg->method());
                if (addrs.empty())
                {
                    rsp->setRCode(RCode::RCODE_NOT_FOUND_SERVICE);
                    return conn->send(rsp);
                }

                rsp->setHost(addrs);
                rsp->setRCode(RCode::RECODE_OK);
                rsp->setMethod(msg->method());

                return conn->send(rsp);
            }

        private:
            ProviderManager::ptr m_provider_manager;
            DiscoverManager::ptr m_discoverer_manager;
        };
    }
}